/*
 * Copyright (c) 2007-2020 西安交通信息投资营运有限公司 版权所有
 */

package com.xci.edu.core.service;

import com.github.lvyanyang.annotation.BindingOperateUser;
import com.github.lvyanyang.annotation.OperateLog;
import com.github.lvyanyang.sys.core.BaseSysService;
import com.xci.edu.core.entity.*;
import com.xci.edu.core.dao.EduStudentDao;
import com.xci.edu.core.filter.EduStudentFilter;
import com.github.lvyanyang.core.BoolResult;
import com.github.lvyanyang.core.PageList;
import com.github.lvyanyang.core.XCI;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;

/**
 * 学员信息服务
 * @author 张谦 zhangqian@xci96716.com
 * @since 2020-11-24 15:29:29
 */
@Service
public class EduStudentService extends BaseSysService {
    /** 学员信息数据层对象 */
    @Resource private EduStudentDao eduStudentDao;
    /** 学员培训日志服务 */
    @Resource private EduStudentTrainLogService eduStudentTrainLogService;
    /** 培训计划服务 */
    @Resource private EduPlanService eduPlanService;
    /** 培训科目信息服务 */
    @Resource private EduCategoryService eduCategoryService;
    /** 考试预约服务 */
    @Resource private ExamPreService examPreService;

    /** 学员培训日志对象 */
    private EduStudentTrainLog eduStudentTrainLog;

    /** 学生培训操作时 培训日志msg配置 */
    private String EduStudentTrainLogMsg_delete ="学员信息删除";
    private String EduStudentTrainLogMsg_update ="学员基础信息修改";
    private String EduStudentTrainLogMsg_0 ="预报名";
    private String EduStudentTrainLogMsg_0_1 ="学生信息审核";
    private String EduStudentTrainLogMsg_1 ="报名";
    private String EduStudentTrainLogMsg_2 ="设置培训计划";
    private String EduStudentTrainLogMsg_2_deletePlan ="退档";
    private String EduStudentTrainLogMsg_3 ="参加培训学习";
    private String EduStudentTrainLogMsg_4 ="考试预约";
    private String EduStudentTrainLogMsg_5 ="结业";

    /**
     * 生成新学员培训日志对象
     *
     */
    private EduStudentTrainLog newEduStudentTrainLog(EduStudent eduStudent,String msg) {
        eduStudentTrainLog = new EduStudentTrainLog();
        eduStudentTrainLog.setOperateDateTime(new Date());
        eduStudentTrainLog.setOperateDate(new Date());
        eduStudentTrainLog.setStudentId(eduStudent.getId());
        eduStudentTrainLog.setStudentName(eduStudent.getName());
        eduStudentTrainLog.setMsg(msg);
        return eduStudentTrainLog;
    }

    /**
     * 新建学员信息
     * @param entity 学员信息实体
     */
    @BindingOperateUser
    @OperateLog(msg = "新建学员信息", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult insert(EduStudent entity) {
        String msg ="";
        if(entity.getStatus()==0){
            //预报名
            msg = EduStudentTrainLogMsg_0;
        }else{
            //报名
            msg =EduStudentTrainLogMsg_1;
        }
        return save(entity, true,msg);
    }

    /**
     * 修改学员信息
     * @param entity 学员信息实体
     */
    @BindingOperateUser
    @OperateLog(msg = "修改学员信息", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult update(EduStudent entity) {
        return save(entity, false,EduStudentTrainLogMsg_update);
    }

    ///**
    // * 批量保存学员信息
    // * @param entities 学员信息实体集合
    // */
    //@OperateLog(msg = "修改学员信息", module = "学员信息")
    //@Transactional(rollbackFor = Exception.class)
    //public void batchSave(List<EduStudent> entities) {
    //    for (EduStudent entity : entities) {
    //        if (entity.getId() == null || !eduStudentDao.existById(entity.getId())) {
    //            //无主键或者主键在数据库中不存在,则新建;
    //            self.save(entity, true).ifFailThrow();
    //        } else {
    //            //其他情况,则修改
    //            self.save(entity, false).ifFailThrow();
    //        }
    //    }
    //}

    /**
     * 删除学员信息
     * @param ids 学员信息主键字符串
     */
    @OperateLog(msg = "删除学员信息", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult delete(@NotBlank(message = "请指定学员信息主键字符串") String ids) {
        XCI.forEachIds(ids, id -> {
            var entity = eduStudentDao.selectById(id);
            if (entity == null) return;

            //删除业务的验证
            //业务:学员只能在---预报名---才能删除
            if(!entity.getStatus().equals(0)){
                XCI.throwException(BoolResult.fail(XCI.format(" {}({})无法删除,原因：只有预报名状态才能删除!", entity.getName(),entity.getIdNumber())));
            }

            eduStudentDao.deleteById(entity.getId());
            //保存培训日志信息
            newEduStudentTrainLog(entity,EduStudentTrainLogMsg_delete);
            eduStudentTrainLogService.insert(eduStudentTrainLog);
        });
        return BoolResult.success();
    }

    /**
     * 是否存在指定科目的学员信息
     * @param eduCategoryId 科目主键
     * @return 如果存在返回true
     */
    public boolean existxByEduCategoryId(@NotNull(message = "请指定科目主键") Long eduCategoryId) {
        return eduStudentDao.existxByEduCategoryId(eduCategoryId);
    }

    /**
     * 是否存在指定培训计划的学员信息
     * @param eduPlanId 计划主键
     * @return 如果存在返回true
     */
    public boolean existxByEduPlanId(@NotNull(message = "请指定计划主键") Long eduPlanId) {
        return eduStudentDao.existxByEduPlanId(eduPlanId);
    }

    /**
     * 根据主键查询单个学员信息
     * @param id 学员信息主键
     * @return 返回学员信息实体
     */
    public EduStudent selectById(@NotNull(message = "请指定学员信息主键") Long id) {
        return eduStudentDao.selectById(id);
    }

    /**
     * 查询学员信息列表
     * @param filter 过滤条件
     * @return 返回学员信息列表
     */
    public List<EduStudent> selectList(EduStudentFilter filter) {
        return eduStudentDao.selectList(filter);
    }

    /**
     * 查询学员信息分页列表
     * @param filter 过滤条件
     * @return 返回学员信息分页列表
     */
    public PageList<EduStudent> selectPageList(EduStudentFilter filter) {
        return PageList.of(eduStudentDao.selectPageList(filter));
    }

    /**
     * 保存数据,在insert和update之前和之后的校验和处理
     * @param entity  学员信息实体
     * @param created 是否新建
     * @param msg 培训日志信息
     */
    private BoolResult save(EduStudent entity, boolean created ,String msg) {
        //新建时如果主键未指定,自动设置主键
        XCI.ifTrueAction(XCI.invalidId(entity.getId(), created), () -> entity.setId(XCI.nextId()));
        //如果简拼为空,设置简拼
        XCI.ifBlankAction(entity.getSpell(), () -> entity.setSpell(XCI.getSpell(entity.getName())));

        //检测学生信息
        BoolResult boolResult =  checkEntity(entity,created);
        if (boolResult.isFail()) {
            return boolResult;
        }

        //更新数据库
        if (created) {
            eduStudentDao.insertDynamic(entity);
        } else {
            if(msg.equals(EduStudentTrainLogMsg_update)){
                //学员基础信息修改
                //业务：只有学员在预报名和报名状态才能修改
                EduStudent before = eduStudentDao.selectById(entity.getId());
                if(!before.getStatus().equals(0) && !before.getStatus().equals(1)){
                   return BoolResult.fail(XCI.format(" {}({})学员基础信息无法修改,原因：只有预报名、报名状态才能修改", entity.getName(),entity.getIdNumber()));
                }
            }
            if(msg.equals(EduStudentTrainLogMsg_2_deletePlan)){
                eduStudentDao.updatePlanIdAndPlanName(entity.getId());
            }else{
                eduStudentDao.updateDynamic(entity);
            }
        }
        //记录培训日志
        newEduStudentTrainLog(entity,msg);
        eduStudentTrainLogService.insert(eduStudentTrainLog);
        return BoolResult.success();
    }

    /**
     * 检测学生信息
     *
     * @param entity  学生对象
     * @param created 是否新建
     */
    private BoolResult checkEntity(EduStudent entity, boolean created) {
        // 身份证 唯一性验证
        if (eduStudentDao.existxByIdNumber(entity.getIdNumber(), created ? null : entity.getId())) {
            return BoolResult.fail(XCI.format(" {}({})已经在学习,无法重复报名", entity.getName(),entity.getIdNumber()));
        }

        return BoolResult.success();
    }

    /**
     * 学生信息审核
     * @param ids 学员信息主键字符串
     */
    @OperateLog(msg = "学生信息审核", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult auditStudent(@NotNull(message = "请指定学员信息主键字符串") String ids) {
        XCI.forEachIds(ids, id -> {
            EduStudent beforeEduStudent = eduStudentDao.selectById(id);
            //学生信息审核的业务的验证
            BoolResult boolResult = beforeAuditStudent(beforeEduStudent);
            if (boolResult.isFail()) {
                XCI.throwException(boolResult);
            }

            //修改学生信息
            EduStudent eduStudent = new EduStudent();
            eduStudent.setId(id);
            eduStudent.setName(beforeEduStudent.getName());
            eduStudent.setStatus(1);
            save(eduStudent,false,EduStudentTrainLogMsg_0_1);
        });
        return BoolResult.success();
    }

    /**
     * 学生信息审核的业务的验证
     *
     * @param entity  学生对象
     */
    private BoolResult beforeAuditStudent(EduStudent entity) {
        // 学生是否是预报名
        if (!entity.getStatus().equals(0)) {
            return BoolResult.fail(XCI.format(" {}({})--不是预报名,无法审核!", entity.getName(),entity.getIdNumber()));
        }
        return BoolResult.success();
    }

    /**
     * 退档
     * @param ids     学员信息主键字符串
     */
    @OperateLog(msg = "退档", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult setStatusOne(@NotNull(message = "请指定学员信息主键字符串") String ids) {
        XCI.forEachIds(ids, id -> {
            EduStudent beforeEduStudent = eduStudentDao.selectById(id);
            //学生退档业务的验证
            BoolResult boolResult;
            boolResult = beforesetStatusOne(beforeEduStudent);
            if (boolResult.isFail()) {
                XCI.throwException(boolResult);
            }

            //修改培训计划
            boolResult =eduPlanService.removePlan(beforeEduStudent.getPlanId());
            if (boolResult.isFail()) {
                XCI.throwException(boolResult);
            }

            //修改学生信息
            EduStudent eduStudent = new EduStudent();
            eduStudent.setId(id);
            eduStudent.setName(beforeEduStudent.getName());
            save(eduStudent,false,EduStudentTrainLogMsg_2_deletePlan);
        });

        return BoolResult.success();
    }

    /**
     * 学生退档业务的验证
     *
     * @param entity  学生对象
     */
    private BoolResult beforesetStatusOne(EduStudent entity) {
        //退档业务的验证
        if(entity.getStatus().equals(0) || entity.getStatus().equals(1) || entity.getStatus().equals(5)){
            return BoolResult.fail(XCI.format(" {}({})无法退当,原因：预报名、报名、完成状态不能退档!", entity.getName(),entity.getIdNumber()));
        }
        return BoolResult.success();
    }

    /**
     * 设置培训计划 密码
     * @param ids    学员信息主键字符串
     * @param planId 计划主键
     * @param planName 计划名称
     */
    @OperateLog(msg = "修改学员培训计划", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult setPlanUpdate(@NotNull(message = "请指定学员信息主键字符串") String ids,@NotNull(message = "请指定计划主键") long planId,@NotBlank(message = "请指定计划名称") String planName) {
        XCI.forEachIds(ids, id -> {
            EduStudent beforeEduStudent = eduStudentDao.selectById(id);
            //设置培训计划的业务的验证
            // 查看学生当前阶段是否是报名中
            if (!beforeEduStudent.getStatus().equals(1)) {
                XCI.throwException(XCI.format(" {}({})无法设置培训计划,原因：只有报名状态才能设置培训计划!", beforeEduStudent.getName(),beforeEduStudent.getIdNumber()));
            }
            //修改培训计划
            BoolResult boolResult =eduPlanService.AddPlan(planId);
            if (boolResult.isFail()) {
                XCI.throwException(boolResult);
            }
            //生成学生的密码
            String pwd = generatePwd(beforeEduStudent.getIdNumber(),beforeEduStudent.getSpell());
            //修改学生信息
            EduStudent eduStudent = new EduStudent();
            eduStudent.setId(id);
            eduStudent.setName(beforeEduStudent.getName());
            eduStudent.setPlanId(planId);
            eduStudent.setPlanName(planName);
            //如果初次设置培训计划-->状态改成学习中
            eduStudent.setStatus(2);
            eduStudent.setPwd(pwd);
            save(eduStudent,false, EduStudentTrainLogMsg_2);
        });
        return BoolResult.success();
    }

    /**
     * 生成学生密码
     *
     * @param idNumber  学生身份证
     * @param spell  学生姓名简拼
     */
    private String generatePwd(String idNumber,String spell) {
        String pwd = idNumber.substring(idNumber.length() - 6);
        String newPwd = XCI.encryptPassword(idNumber, pwd,spell);
        return newPwd;
    }

    /**
     * 学员培训-->修改学员培训总学时
     *
     * @param studentId  学员主键
     * @param period 学时
     */
    @OperateLog(msg = "修改学员培训总学时", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult setTrainTotalPeriod(@NotNull(message = "请指定学员主键") Long studentId,@NotNull(message = "请指定学时(秒)") Integer period) {
        EduStudent beforeEduStudent = eduStudentDao.selectById(studentId);
        //总学时
        Integer trainTotalPeriod;
        //学时计算
        if(!beforeEduStudent.getTrainTotalPeriod().equals(0)){
            trainTotalPeriod = beforeEduStudent.getTrainTotalPeriod()+period;
        }else{
            trainTotalPeriod = period;
        }

        EduStudent eduStudent =new EduStudent();
        eduStudent.setId(studentId);
        eduStudent.setName(beforeEduStudent.getName());
        eduStudent.setTrainTotalPeriod(trainTotalPeriod);

        //计算如果总学时>计划总学时 -->状态修改为 准备考试
        EduCategory eduCategory = eduCategoryService.selectById(beforeEduStudent.getEduCategoryId());
        if(eduCategory.getTotalPeriod() <=  trainTotalPeriod){
            eduStudent.setStatus(3);
        }
        //更改学生基础信息
        save(eduStudent,false,EduStudentTrainLogMsg_3);
        return BoolResult.success();
    }

    /**
     * 预约考试
     * @param ids    学员信息主键字符串
     * @param examPre  考试预约对象
     */
    @OperateLog(msg = "预约考试", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult setExamProUpdate(@NotNull(message = "请指定学员信息主键字符串") String ids,ExamPre examPre) {
        XCI.forEachIds(ids, id -> {
            EduStudent beforeEduStudent = eduStudentDao.selectById(id);
            //设置培训计划的业务的验证
            BoolResult boolResult = beforesetExamProUpdateCheck(beforeEduStudent);
            if (boolResult.isFail()) {
                XCI.throwException(boolResult);
            }
            //更改学生基础信息
            EduStudent eduStudent =new EduStudent();
            eduStudent.setId(id);
            eduStudent.setName(beforeEduStudent.getName());
            eduStudent.setStatus(4);
            save(eduStudent,false,EduStudentTrainLogMsg_4);

            examPre.setId(null);
            examPre.setStudentId(id);
            examPre.setStudentName(beforeEduStudent.getName());
            //保存考试预约
            examPreService.insert(examPre);
        });
        return BoolResult.success();
    }

    /**
     * 预约考试业务的验证
     *
     * @param entity  学生对象
     */
    private BoolResult beforesetExamProUpdateCheck(EduStudent entity) {
        // 查看学生当前阶段是否是   准备考试  或者 考试中阶段
        //准备考试_3", "考试中_4
        if (!entity.getStatus().equals(3) && !entity.getStatus().equals(4)) {
            return BoolResult.fail(XCI.format(" {}({})无法预约考试,原因：只有准备考试、考试中状态才能设置预约考试!", entity.getName(),entity.getIdNumber(),entity.getStatusText()));
        }
        return BoolResult.success();
    }

    /**
     * 完成学业
     *
     * @param studentId  学员主键
     */
    @OperateLog(msg = "学员完成学业", module = "学员信息")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult graduation(@NotNull(message = "请指定学员主键") Long studentId) {
        EduStudent beforeEduStudent = eduStudentDao.selectById(studentId);
        //更改学生基础信息
        eduStudentDao.graduation(studentId);
        //保存培训日志信息
        newEduStudentTrainLog(beforeEduStudent,EduStudentTrainLogMsg_5);
        eduStudentTrainLogService.insert(eduStudentTrainLog);
        return BoolResult.success();
    }
}